package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.wxpay.plus.WxPayParam;
import com.github.wxpay.plus.WxPayTemplate;
import com.lkd.common.VMSystem;
import com.lkd.config.ConsulConfig;
import com.lkd.config.TopicConfig;
import com.lkd.contract.OrderContract;
import com.lkd.contract.VendoutContract;
import com.lkd.contract.VendoutData;
import com.lkd.dao.OrderDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.OrderEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.service.OrderService;
import com.lkd.vo.PartnerVO;
import com.lkd.vo.PayReq;
import com.lkd.vo.SkuVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Map;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Override
    public OrderEntity getByOrderNo(String orderNo) {
        QueryWrapper<OrderEntity> qw = new QueryWrapper<>();
        qw.lambda()
                .eq(OrderEntity::getOrderNo,orderNo);
        return this.getOne(qw);
    }

    @Autowired
    private VMService vmService;

    @Autowired
    private UserService userService;

    @Autowired
    private MqttProducer mqttProducer;

    @Autowired
    private ConsulConfig consulConfig;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;


    @Autowired
    private RestHighLevelClient esClient;

    @Autowired
    private WxPayTemplate wxPayTemplate;

    /*
    * 业务分析：
    *  1.判断关键数据
    *   innerCode、skuId
    *  2.判断业务数据
    *    售货机数据
    *       售货机是否存在
    *       售货机是否有存货
    *    商品数据
    *       判断商品是否存在
    *  3.创建订单数据
    *       售货机信息赋值
    *       商品信息赋值
    *       订单状态的赋值、合作商以及分成、支付状态、订单编号、openid
    *
    *  4.调用第三方支付获得结果
    *  5.发送订单延时消息
    * */
    @Override
    public Map<String, String> createOrderAndWxPayOrder(PayReq payReq) {


        //1.判断关键数据
        //  innerCode、openId、skuId
        if (StringUtils.isEmpty(payReq.getInnerCode())
                || StringUtils.isEmpty(payReq.getSkuId())
        ) {
            throw new LogicException("传入参数与接口不匹配");
        }



        // 2.判断业务数据
        //   售货机数据
        //      售货机是否存在
        //      售货机运营状态
        //      售货机是否有存货
        VmVO vm = vmService.getVMInfo(payReq.getInnerCode());
        if (ObjectUtils.isEmpty(vm)) {
            throw new LogicException("售货机不存在");
        }
        if (!(ObjectUtils.nullSafeEquals(VMSystem.VM_STATUS_RUNNING,vm.getVmStatus()))) {
            throw new LogicException("售货机运营状态异常");
        }
        if(!vmService.hasCapacity(payReq.getInnerCode()
                ,Long.valueOf(payReq.getSkuId()))){
            throw new LogicException("该商品已售空");
        }


        //   商品数据
        //      判断商品是否存在
        SkuVO skuVo = vmService.getSku(payReq.getSkuId());
        if (ObjectUtils.isEmpty(skuVo)) {
            throw new LogicException("商品信息不存在");
        }

        // 3.创建订单数据
        //    售货机信息赋值
        //    商品信息赋值
        //    订单状态的赋值、合作商以及分成、支付状态、订单编号、openid


        OrderEntity orderEntity = new OrderEntity();

        //售货机数据编复制给订单对象
        BeanUtils.copyProperties( vm,orderEntity  );
        orderEntity.setAddr(vm.getNodeAddr());

        //商品复数据制给订单对象
        BeanUtils.copyProperties( skuVo,orderEntity  );
        orderEntity.setAmount(skuVo.getRealPrice());//价格


        orderEntity.setStatus(VMSystem.ORDER_STATUS_CREATE);
        //计算合作商账单分账金额
        PartnerVO partner = userService.getPartner(vm.getOwnerId());
        BigDecimal bg = new BigDecimal(skuVo.getRealPrice());
        int bill = bg.multiply(new BigDecimal(partner.getRatio())).divide(new BigDecimal("100"),0, RoundingMode.HALF_UP).intValue();
        orderEntity.setBill(bill);


        // 支付状态、订单编号、openid
        orderEntity.setOpenId(payReq.getOpenId());//openId
        orderEntity.setPayStatus(VMSystem.PAY_STATUS_NOPAY);//支付状态
        orderEntity.setOrderNo(payReq.getInnerCode()+System.nanoTime());//订单编号

        this.save(orderEntity);

        // 4.调用第三方支付获得结果
        var wxPayParam=new WxPayParam();
        wxPayParam.setBody(orderEntity.getSkuName());//商品名称
        wxPayParam.setOutTradeNo(orderEntity.getOrderNo());//订单号
        wxPayParam.setTotalFee(orderEntity.getAmount().intValue());//金额
        wxPayParam.setOpenid(orderEntity.getOpenId());//用户id


        // 5.发送订单延时消息
        //将订单放到延迟队列中，5分钟后检查支付状态！！！！！！！！！！！！！！！！！！
        OrderContract orderContract = new OrderContract();
        orderContract.setOrderNo(orderEntity.getOrderNo());
        try {
            mqttProducer.send("$delayed/300/"+TopicConfig.ORDER_CHECK_TOPIC,2,orderContract);
        } catch (Exception e) {
            log.error("send to emq error",e);
        }

        return wxPayTemplate.requestPay( wxPayParam);  //调用微信支付
    }



    @Override
    public boolean vendout(String orderNo,Long skuId,String innerCode) {
        //封装协议中的数据
        var reqData = new VendoutData();
        reqData.setOrderNo(orderNo);
        reqData.setSkuId(skuId);
        //封装协议
        var contract = new VendoutContract();
        contract.setVendoutData(reqData);
        contract.setInnerCode(innerCode);
        //向售货机微服务  发送出货请求
        try {
            mqttProducer.send( TopicConfig.VMS_VENDOUT_TOPIC,2,contract);
        } catch (JsonProcessingException e) {
            log.error("send vendout req error.",e);
            return false;
        }
        return true;
    }

    /*
    * 业务分析：
    *   1.解析和解析微信通知结果
    *      支付结果
    *   2.判断业务数据
    *       订单
    *           订单是否存在
    *           订单状态是否正常
    *           订单金额是否和支付平台金额一致
    *   3.修改订单的信息
    *       修改订单支付状态
    *       修改订单状态
    *   4.发送出货通知异步消息
    * */
    @Transactional
    public void processPaymentResult(Map<String, String> resultMap) {

        // 1.解析和解析微信通知结果
        //    支付结果
        var code = resultMap.get("code");
        if (!(WxPayTemplate.SUCCESS_FLAG.equals(code))) {
            throw new LogicException("微信通知为失败信息，不做业务操作: error-"+code);
        }



        // 2.判断业务数据
        //     订单
        //         订单是否存在
        //         订单状态是否正常
        //         订单金额是否和支付平台金额一致
        var order = this.getByOrderNo(resultMap.get("order_sn"));

        if (ObjectUtils.isEmpty(order)) {
            throw new LogicException("订单信息不存在");
        }
        if (!(ObjectUtils.nullSafeEquals(order.getStatus(),VMSystem.ORDER_STATUS_CREATE))) {
            throw new LogicException("订单状态异常");
        }

        if (!(resultMap.get("price").equals(order.getAmount().toString()))) {
            throw new LogicException("订单金额和支付平台金额一致");
        }

        // 3.修改订单的信息
        var updateWrapper = new LambdaUpdateWrapper<OrderEntity>();
        updateWrapper.set(OrderEntity::getStatus, VMSystem.ORDER_STATUS_PAYED);
        updateWrapper.set(OrderEntity::getPayStatus, VMSystem.PAY_STATUS_PAYED);
        updateWrapper.set(OrderEntity::getUpdateTime, LocalDateTime.now());
        updateWrapper.eq(OrderEntity::getId, order.getId());

        if (!(this.update(updateWrapper))) {
            throw new LogicException("更改订单支付状态失败");
        }

        // 4.发送出货通知异步消息
        if (!(this.vendout(order.getOrderNo(),order.getSkuId(),order.getInnerCode()))) {
            throw new LogicException("发送出货通知异步消息失败");
        }
    }


    /*
    * 业务处理
    *   1.判断业务数据
    *       订单数据
    *           判断是否存在
    *           判断订单状态
    *   2.将订单改为失效状态
    * */
    @Override
    public void processTimeoutOrder(String orderNo) {

        // 1.判断业务数据
        //     订单数据
        //         判断是否存在
        //         判断订单状态
        //查询订单
        OrderEntity orderEntity = this.getByOrderNo(orderNo);
        if(orderEntity == null){
            log.warn("订单数据不存在 订单号：{}",orderNo);
            return;
        }

        if(orderEntity.getStatus().equals(VMSystem.ORDER_STATUS_CREATE)){  //如果
            log.error("订单状态异常 订单号：{}",orderNo);
        }

        // 2.将订单改为失效状态
        var updateWrapper = new LambdaUpdateWrapper<OrderEntity>();
        updateWrapper.set(OrderEntity::getStatus, VMSystem.ORDER_STATUS_INVALID);
        updateWrapper.set(OrderEntity::getUpdateTime, LocalDateTime.now());
        updateWrapper.eq(OrderEntity::getId, orderEntity.getId());

        if (!(this.update(updateWrapper))) {
            log.error("超时订单修改状态失败，orderNo：{}",orderNo);
        }
    }


}
